CloudWatch RUM を使って NuxtJS アプリのクライアント側のエラーをモニタリングしてみた
はじめに
プロフィールビューアーサービス Proflly(プロフリー)の運用にて、エラーが発生している原因を調査することがあります。その際に再現性があるエラーの場合は、原因を突き止めやすいのですが、再現性がないエラーの場合、調査して原因を突き止めるのが難しい場合があります。 そういった場合に、クライアント側のエラー(Javascript のエラーなど)をモニタリングできると、エラー時の挙動がより調査しやすくなるので、CloudWatch RUM を実際に触って試してみました。
やってみること
- CloudWatch RUM にアプリケーションモニターを作成
- NuxtJS アプリ(Single Page Application)に Amazon CloudWatch RUM Web Client を組み込む
- NuxtJS アプリでエラー(例外)を発生させる
- エラー内容をアプリケーションモニターから確認
実行環境
- Amazon CloudWatch RUM Web Client:
1.1.0
- NuxtJS:
2.15.8
- Node.js:
14.17.1
CloudWatch RUM にアプリケーションモニターを作成
エラー情報を収集するためには、CloudWatch RUM にアプリケーションモニターを作成する必要があります。アプリケーションモニターを作成すると、JavaScript コードスニペットが生成されるので、このスニペットをアプリケーションに組み込む必要があります。
CloudWatch から RUM
を開き、アプリケーションモニターを追加
ボタンをクリックして、アプリケーションモニターの作成を開始します。
基本的に環境に合わせて設定していけば問題なさそうですが、主な設定値は以下の通り設定しました。
- 詳細の指定
- アプリケーションドメイン
- アプリで利用するドメイン名を指定する
dev.classmethod.jp
というドメイン名を利用したアプリの場合は、dev.classmethod.jp
を指定するか、classmethod.jp
を指定して、サブドメインを含める
をチェックしておけば大丈夫そうです
- アプリケーションドメイン
- RUM データ収集を設定する
- JavaScript エラー
チェック
- 今回 JavaScript のエラーをモニタリングしたいので、チェックを入れる
- JavaScript エラー
- Cookie を許可する
チェック
チェック
することで、ユーザーやセッションごとにエラー情報を集約してくれるようです
- セッションサンプル
100
- 承諾
新しい ID プールを作成する
- CloudWatch RUM にデータを送信する際に認証とアクセス許可が必要となりますので、そのプロバイダを設定します。
新しい ID プールを作成する
を選択すると、Cognito ID プールが新規で作成され、設定されているロールを引き受けることによりPutRumEvents
API を実行することが許可されるようです。既存の Cognito ID プールの利用することや、サードパーティのプロバイダーを設定することもできるようです。
- CloudWatch RUM にデータを送信する際に認証とアクセス許可が必要となりますので、そのプロバイダを設定します。
各項目を設定し、作成すると JavaScript コードスニペット
が生成されるので、コピーまたはダウンロードすることができます。(後からでもコピーまたはダウンロードすることができます)
また、コードスニペットはアプリケーションモニターにて指定したパラメータを元に作成されていますが、Amazon CloudWatch RUM Web Client に指定するパラメータをカスタマイズすることができます。指定できるパラメータについてはこちらを参照ください。
NuxtJS アプリに Amazon CloudWatch RUM Web Client を組み込む
JavaScript コードスニペットを組み込むことで、Amazon CloudWatch RUM Web Client を CDN からインストールします。NuxtJS アプリのすべての画面に JavaScript コードスニペットを組み込みたいので、nuxt.config.js
の head
で読み込むように組み込んでみました。
組み込み方法は、アプリケーションの構成によって組み込みやすい方法でよいかと思いますが、body
要素またはその他のscript
要素より前に組み込む必要があるようです。
今回は、JavaScript コードスニペットをダウンロードし static/rumTracker.js
として配置し他ファイルを読み込むようにしました。
なお、ダウンロードしたファイルの中身を確認すると、script
タグが含まれていたので、今回のように JavaScript のファイルとして読み込む場合は、script
タグを削除しておいたほうが良さそうです。
export default { ... // Global page headers: https://go.nuxtjs.dev/config-head head: { ... link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], script: [ {src: '/rumTracker.js'} ] }, ... }
これで基本的に組み込みは良いのですが、NuxtJS のようにフレームワークを利用している場合、ハンドリングしていない JavaScript エラーをインターセプトしていることがあります。エラーの収集は、アプリケーションでハンドリングしていないエラーが収集対象となるので、このままだとエラーとしてデータが収集されません。
そこで、recordError
コマンドを利用して、エラーデータを収集します。今回は、エラーをハンドリングするプラグインを作成して、その処理の中で recordError
コマンドを実行するように組み込んでみました。
import Vue from 'vue' declare function cwr(operation: string, payload: any): void export default (context: any, inject: any) => { Vue.config.errorHandler = (err, vm, info) => { console.error(err) cwr('recordError', err) // エラーハンドリング処理 } }
export default { ... plugins: [ {src: '~/plugins/error-handler'} ], ... }
NuxtJS アプリをデプロイし、エラー(例外)を発生させる
ここまででアプリケーションへの組み込みは、完了したので、実際にエラーを発生させる処理を実装して、デプロイします。今回は、S3 + CloudFront + Route 53 で独自ドメインにデプロイしました。なお、ドメインはアプリケーションモニターを作成した時に指定したドメインとしています。ここが合っていないと、正しく情報が収集されませんので注意が必要です。 デプロイが完了したら、実際にエラー(例外)を発生させてみます。
エラー内容をアプリケーションモニターから確認
エラーを記録したので、CloudWatch から確認してみます。反映まで最大で15分とドキュメントに記載がありましたが、実際に試してみるとおおよそ1,2分程度で確認することができました。
対象のエラーのセッションをたどっていくと、エラーのスタックトレースを確認することができます。
これくらいの情報がわかれば、クライアントでなにが起こっているのか?を判断するのに役立ちそうですね!
さいごに
今回は調査の際に、サーバー側のログだけでは、なかなか原因がつかめない場合などにクライアント側のエラー情報を確認したいと思い、調査してみました。実際に触ってみると、アプリケーションに組み込むのも簡単ですし、コストも収集する情報やサンプリングするレートである程度調整できるので、プロダクション環境でぜひ利用してみたいなぁと思いました。
いろいろなクライアントエラーの収集方法がある中の、手段の1つとして、どなたかの参考になれば幸いです。